# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import sys
import time
from optparse import OptionParser
import memreport
import grapher
from subprocess import Popen, PIPE

def logger(iterations, wait, source="/proc", chrome_only=False):
  """ Logs smaps behavior of chrome and non-chrome processes.

  Required arguments:
  iterations -- number of memory data snapshots to take
  wait -- seconds between each data capture
  chrome -- bool decides if data from all or only chrome processes is acquired

  Keyword arguments:
  source -- the directory used for parsing (default: /proc)
  chrome_only -- If True, does not account for non-Chrome process

  Returns:
  snapshots_list -- list with ProcSnapshot objects
  """
  count = 0
  snapshots_list = []
  while count < iterations:
    snapshot = memreport.ProcSnapshot(source, chrome_only)
    #TODO: Decide what information we want to present instead of saving
    # all the data available
    snapshots_list.append(snapshot)
    count += 1
    if count < iterations:
      time.sleep(wait)

  return snapshots_list

def userArgs():
  """
  Function uses the OptionParser object to allow for voluntary user input from
  the command line.
  """
  parser = OptionParser()
  parser.add_option("-p", "--proc", type="string", metavar="SOURCE DIR",
                    dest="source", help="source directory [default: /proc]",
                    default="/proc")
  parser.add_option("-i", "--iterations", type='int', default=1,
                     help="number of snapshot iterations")
  parser.add_option("-t", "--wait_time", type='int', default=0,
                    help="time in seconds between each snapshot",
                    dest="interval")
  parser.add_option("-c", "--chrome_only", action="store_true", default=False,
                    help="show only chrome processes")
  parser.add_option("-g", "--graph", default="timelog",
                    help="choose graph type (timelog, snapshot, rss)")
  parser.add_option("-m", "--memory", default="Pss",
                    help="choose memory type to display")

  #TODO: add "save file to destination" option + generate it on the fly
  return parser.parse_args()


def setTimelogDataTable(snapshots_list, mem_type, show_gpu=False, uptime=0):
  '''
  Given a list of ProcSnapshots objects, return a list formatted properly for
  a line graph
  '''
  chrome, sys_pids_list = getUniquePidsList(snapshots_list)

  # Doing only for chrome right now. Figure out how to use this for
  # non-Chrome as well
  data_table = []
  data_table.append(["Time"])
  chrome_keys = sorted(chrome.keys(), key=int)
  for pid in chrome_keys:
    data_table[0].append(pid + ': ' + chrome[pid])
  if show_gpu:
    data_table[0].append("Total Graphics Memory")
    data_table[0].append("Current Graphics Memory in GTT")

  i = 0
  for snapshot in snapshots_list:
    i += 1
    data_table.append([int(snapshot.timestamp - snapshots_list[0].timestamp +
                           uptime)])
    # Iterate through all the pids in the first table
    for pid in chrome_keys:
      try:
        data_table[i].append(round(snapshot.chrome_smaps.pid[pid][mem_type] /
                                   1024.0, 1))
      except KeyError:
        data_table[i].append(0)
    if show_gpu:
      data_table[i].append(round(snapshot.gpu_mem.total / 1024.0, 1))
      data_table[i].append(round(snapshot.gpu_mem.current / 1024.0, 1))

  return data_table


def getUniquePidsList(snapshots_list):
  '''
  Return a sorted list with all the unique PIDs in a list of ProcSnapshot
  objects.
  '''
  # Create the two sets
  #chrome_set = set()
  sys_set = set()

  chrome = {}

  # Iterate through snapshots list
  for snapshot in snapshots_list:

    # Add chrome PIDs to set
    for pid in snapshot.chrome_pids_list:
      chrome[pid] = snapshot.chrome_types[pid]
      #chrome_set.add(pid)

    # Add non-Chrome PIDs to set
    for pid in snapshot.sys_pids_list:
      sys_set.add(pid)

  # Return sorted list of the sets
  return chrome, sorted(sys_set, key=int)


def setSnapshotDataTable(snapshot, mem_type, chrome_only):
  '''
  Given a list of ProcSnapshots objects, return a list formatted properly for
  a pie graph
  '''
  data_table = []
  data_table.append(["PID", "Size"])
  mem_type = mem_type.capitalize()
  sorted_chrome = snapshot.chrome_smaps.sortByMemType(mem_type)
  for item in sorted_chrome:
    if item[0] != "Total":
      data_table.append([item[0], item[1][mem_type]])

  if not chrome_only:
    sorted_sys = snapshot.sys_smaps.sortByMemType(mem_type)
    for item in sorted_sys:
      if item[0] != "Total":
        data_table.append([item[0], item[1][mem_type]])

  return data_table

def setRssDataTable(snapshot, chrome_only):
  '''
  Given a list of ProcSnapshots objects, return a list formatted properly for
  a bar graph
  '''
  data_table = []
  data_table.append(["PID", "Private", "Shared"])
  for pid in (snapshot.chrome_pids_list + ["Total"]):
    private = snapshot.chrome_smaps.pid[pid]["Private"]
    shared = snapshot.chrome_smaps.pid[pid]["Shared"]
    data_table.append([pid, private, shared])

  if not chrome_only:
    for pid in (snapshot.sys_pids_list + ["Total"]):
      private = snapshot.sys_smaps.pid[pid]["Private"]
      shared = snapshot.sys_smaps.pid[pid]["Shared"]
      data_table.append([pid, private, shared])

  return data_table

def getSystemParameters():
  sys_info = {}

  # first, get uname
  sys_info["uname"] = Popen("uname -a", shell=True,
                            stdout=PIPE).stdout.readline()

  # now get lsb info
  lsb_f = open("/etc/lsb-release", 'r')
  lsb = lsb_f.readlines()
  lsb_f.close()
  for line in lsb:
    line = line.split('=')
    try:
      sys_info[line[0]] = line[1]
    except IndexError:
      pass

  return sys_info

# Main Function
#TODO(thiagog): Add command line support to input all required fields for
# graph creation.
if __name__ == "__main__":
  options, args = userArgs()

  if options.graph not in ("rss", "snapshot", "timelog"):
    sys.exit("ERROR: graph argument invalid")

  uptime_file = open("/proc/uptime", 'r')
  uptime = float(uptime_file.readline().split()[0])
  uptime_file.close()

  sys_info = getSystemParameters()

  timestamp = time.asctime(time.localtime())
  snapshots = logger(options.iterations, options.interval,
                     options.source, options.chrome_only)


  gpu_mem = ("Pss", "Rss")

  # For testing right now
  mem_type = "Pss"


  # Testing on linux machine (no gpu memory information)
  show_gpu = False

  html_args = {}

  if options.graph == "timelog":
    graphs = ("Pss", "Rss", "Private_Clean", "Private_Dirty", "Shared_Clean",
              "Shared_Dirty", "Private", "Shared", "Referenced", "Anonymous")
    for mem_type in graphs:
      show_gpu = mem_type in gpu_mem
      html_args[mem_type] = setTimelogDataTable(snapshots, mem_type, show_gpu,
                                                uptime)

  if options.graph == "snapshot":
    snapshot = snapshots[0]
    html_args["title"] = mem_type + " [kBs] per PID"
    html_args["data"] = setSnapshotDataTable(snapshot, mem_type,
                                        options.chrome_only)

  if options.graph == "rss":
    snapshot = snapshots[0]
    html_args["title"] = "Private and Shared memory usage [kBs] per PID"
    html_args["haxis"] = "PIDs"
    html_args["data"] = setRssDataTable(snapshot, True)

  html_args["version"] = sys_info["uname"]
  html_args["release"] = sys_info["CHROMEOS_RELEASE_DESCRIPTION"]
  html_args["timestamp"] = timestamp
  #print html_args["data"]
  print grapher.chooseGraph(options.graph, html_args)
